﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using EffiProz.Core;
using EffiProz.Core.Persist;
using EffiProz.Core.Results;
using EffiProz.Core.Errors;
using EffiProz.Core.Server;


namespace System.Data.EffiProz
{
    internal class EfzConnectionProxy : IDisposable
    {
        public SessionInterface SessionProxy { set; get; }
        public EfzConnection Owner { get; set; }
        public EfzConnectionOptions ConnectionOptions { get; set; }
        public EfzTransaction Transaction { get; set; }
        public bool IsPooled { get; set; }
        public long Created { get; set; }
        public long LifeTime { get { return ConnectionOptions.ConnectionLifeTime * TimeSpan.TicksPerSecond; } }   
#if !PLATFORM_COMPACTFRAMEWORK
        public EfzEnlistment Enlistment { get; set; }
#endif
        private bool _disposed = false;
       
        public bool TransactionActive
        {
            get
            {
                return Transaction != null && !Transaction.IsCompleted;
            }
        }

        public bool VerifyConnection()
        {          
            try
            {
                return SessionProxy.isClosed();
            }
            catch
            {
                return false;
            }
        }

        public EfzConnectionProxy(EfzConnectionOptions connectionOptions)
            : this(connectionOptions, null)
        {
        }

        public EfzConnectionProxy(EfzConnectionOptions connectionOptions, EfzConnection owner)
        {
            ConnectionOptions = connectionOptions;
            Owner = owner;
            Created = DateTime.UtcNow.Ticks;
            IsPooled = false;
        }


        public void Close()
        {
            if (SessionProxy != null)
            {
                try
                {
                    SessionProxy.close();
                }
                catch (Exception)
                {
                }
                finally
                {
                    SessionProxy = null;
                    Owner = null;
                    IsPooled = false;
                    Created = 0;
                    ConnectionOptions = null;
                }
            }
        }

        public void Open()
        {
            EffiProzProperties props = CreateDBProperties();
            
            props.setProperty(EffiProzDatabaseProperties.url_shutdown, ConnectionOptions.AutoShutdown);

            try
            {
                if (DatabaseURL.isInProcessDatabaseType(ConnectionOptions.ConnectionType))
                {

                    SessionProxy = DatabaseManager.newSession(ConnectionOptions.ConnectionType,
                        ConnectionOptions.Database, ConnectionOptions.User, ConnectionOptions.Password,
                        props, "", 0);
                }
                else if (ConnectionOptions.ConnectionType == DatabaseURL.S_EFZ)
                {
                    SessionProxy = new ClientConnection(ConnectionOptions.Host, ConnectionOptions.Port, ConnectionOptions.Database,
                            ConnectionOptions.Database, ConnectionOptions.Ssl,
                            ConnectionOptions.User, ConnectionOptions.Password, 0);                  
                }
                else if (ConnectionOptions.ConnectionType == DatabaseURL.S_HTTP)
                {
                    SessionProxy = new ClientConnectionHTTP(ConnectionOptions.Host, ConnectionOptions.Port, ConnectionOptions.Database,
                            ConnectionOptions.Database,  ConnectionOptions.Ssl,
                            ConnectionOptions.User, ConnectionOptions.Password, 0);

                }
                else
                {    // alias: type not yet implemented
                    throw EfzException.GetException(ErrorCode.ADO_INVALID_ARGUMENT,
                        ConnectionOptions.ConnectionType);
                }

                if(!ConnectionOptions.AutoCommit)
                    SessionProxy.setAutoCommit(ConnectionOptions.AutoCommit);

                if (ConnectionOptions.Readonly)
                    SessionProxy.setReadOnlyDefault(ConnectionOptions.Readonly);

                if (ConnectionOptions.IsolationLevel != IsolationLevel.ReadCommitted)
                    SessionProxy.setIsolationDefault((int)ConnectionOptions.IsolationLevel);
            }
            catch (CoreException e)
            {
                throw EfzException.GetException(e);
            }
        }
#if !PLATFORM_COMPACTFRAMEWORK
        public void EnlistTransaction(System.Transactions.Transaction transaction)
        {
            if (Owner != null && ConnectionOptions.Enlist)
            {
                if (TransactionActive && transaction != null)
                {
                    throw new ArgumentException("Unable to enlisttransaction (local transaction already exists)");
                }
                if(Enlistment != null && transaction == Enlistment.Scope)
                    return;
                else if(Enlistment != null)
                {
                    throw new ArgumentException("Already enlisted in a transaction");
                }

                this.Enlistment = new EfzEnlistment(this, transaction);  
            }
        }
#endif
        public EfzTransaction BeginTransaction(IsolationLevel level, bool readOnly)
        {
            lock (this)
            {
                if (TransactionActive)
                {
                    throw new InvalidOperationException("Transaction is aleady active.");
                }

                try
                {                   
                    Transaction = new EfzTransaction(Owner, level, readOnly);                  
                }
                catch (CoreException ex)
                {
                    throw EfzException.GetException( ex);
                }
            }

            return Transaction;
        }

        internal void SetReadOnly(bool readOnly)
        {
            SessionProxy.setReadOnlyDefault(readOnly);
        }

        internal void SetAutoCommit(bool autoCommit)
        {
            SessionProxy.setAutoCommit(autoCommit);
        }

        internal void SetIsolationLevel(IsolationLevel isolationLevel)
        {
            SessionProxy.setIsolationDefault((int)isolationLevel);
        }

        public EfzTransaction BeginTransaction(IsolationLevel isolationLevel)
        {
            switch (isolationLevel)
            {
                case IsolationLevel.ReadUncommitted:
                    return BeginTransaction(IsolationLevel.ReadUncommitted, false);

                case IsolationLevel.RepeatableRead:
                    return BeginTransaction(IsolationLevel.RepeatableRead, false);

                case IsolationLevel.Serializable:
                    return BeginTransaction(IsolationLevel.Serializable, false);
              
                case IsolationLevel.ReadCommitted:
                    return BeginTransaction(IsolationLevel.ReadCommitted, false);

                default:
                    return BeginTransaction(IsolationLevel.ReadCommitted, false);
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                Close();

                if (disposing)
                {
                }
            }

            _disposed = true;
        }

        private EffiProzProperties CreateDBProperties(){
            EffiProzProperties props = new EffiProzProperties();

            if(ConnectionOptions.CryptoType != null)
            {
                props.setProperty(EffiProzDatabaseProperties.url_crypt_type, ConnectionOptions.CryptoType);
                props.setProperty(EffiProzDatabaseProperties.url_crypt_key, ConnectionOptions.CryptoKey);
                props.setProperty(EffiProzDatabaseProperties.url_crypt_iv, ConnectionOptions.CryptoIv);
            }           

            return props;
        }


        public bool IsEnlisted
        {
#if !PLATFORM_COMPACTFRAMEWORK
            get { return Enlistment != null && !Enlistment.IsCompleted; }
#else
            get { return false; }
#endif
        }


        public void DisposeTransaction()
        {
            if (Transaction != null && !IsEnlisted)
            {
                Transaction.Dispose();
                Transaction = null;
            }

            try
            {
                SessionProxy.rollback(false);
            }
            catch (Exception) { }
        }


        internal Result ExecuteDirect(string sql)
        {
            Result resultOut = Result.newExecuteDirectRequest();
            resultOut.setMainString(sql);
            Result resultIn = null;
            try
            {
                resultIn = SessionProxy.execute(resultOut);
                if (resultIn.isError())
                    throw resultIn.getException();
            }
            catch (CoreException e)
            {
                throw EfzException.GetException(e);
            }

            return resultIn;
        }
   
    }
}
